﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using ProgNetComponents.Utils;
using ProgNetComponents.Validation.Rules;
using ProgNetComponents.Validation;

namespace System
{
    /// <summary>
    /// Data extension methods
    /// </summary>
    public static class DataExt
    {
        /// <summary>
        /// Adds the range of columns.
        /// </summary>
        /// <param name="columnsCollection">The columns collection.</param>
        /// <param name="columnType">Type of the column.</param>
        /// <param name="columns">The columns.</param>
        /// <returns></returns>
        public static DataColumn[] AddRange(this DataColumnCollection columnsCollection, Type columnType, params string[] columns)
        {
            List<DataColumn> list = new List<DataColumn>();
            if (columns != null && columnsCollection != null)
            {
                foreach (var c in columns)
                    list.Add(columnsCollection.Add(c, columnType));
            }
            return list.ToArray();
        }
        /// <summary>
        /// Adds the range of columns. Columns will have string type.
        /// </summary>
        /// <param name="columnsCollection">The columns collection.</param>
        /// <param name="columns">The columns.</param>
        /// <returns></returns>
        public static DataColumn[] AddRange(this DataColumnCollection columnsCollection, params string[] columns)
        {
            return columnsCollection.AddRange(typeof(string), columns);
        }
        /// <summary>
        /// Assigns the specified row.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="source">The source.</param>
        /// <param name="SkipColumns">The skip columns.</param>
        public static void Assign(this DataRow row, DataRow source, params string[] SkipColumns)
        {
            if (row == null || source == null ||
                row.Table == null || source.Table == null)
                return;
            foreach (DataColumn col in source.Table.Columns)
            {
                if (col.ReadOnly)
                    continue;
                if (SkipColumns != null && SkipColumns.Contains(col.ColumnName))
                    continue;
                if (row.Table.Columns.Contains(col.ColumnName))
                {
                    row[col.ColumnName] = source[col.ColumnName];
                }
            }
        }
        /// <summary>
        /// Updates colum's value if it is different
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static bool SetColVal(this DataRow row, string column, object value)
        {
            if (row != null)
            {
                try
                {
                    if (row[column].IsTheSame(value))
                        return false;
                    row[column] = value;
                    return true;
                }
                catch { }
            }
            return false;
        }

        /// <summary>
        /// Updates colum's value if it is different and performs row.AcceptChanges() if row was not modified before this operation
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static bool SetFakeColval(this DataRow row, string column, object value)
        {
            if (row != null)
            {
                try
                {
                    if (row[column].IsTheSame(value))
                        return false;
                    bool acceptChanges = row.RowState == DataRowState.Unchanged;
                    row[column] = value;
                    if (acceptChanges)
                        row.AcceptChanges();
                    return true;
                }
                catch { }
            }
            return false;
        }

        /// <summary>
        /// Gets the column value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <param name="defVal">The definition value.</param>
        /// <returns></returns>
        public static T GetColumnValue<T>(this DataRow row, string column, T defVal)
        {
            if (row != null && row.Table.Columns.Contains(column))
            {
                if (row[column] is T)
                    return (T)(row[column]);
                else
                {
                    return (T)(row[column].ChangeType(typeof(T)));
                }
            }
            return defVal;
        }
        /// <summary>
        /// Gets the column value.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <returns></returns>
        public static object GetColumnValue(this DataRow row, string column)
        {
            if (row != null && row.Table.Columns.Contains(column))
            {
                return row[column];
            }
            return DBNull.Value;
        }
        /// <summary>
        /// Updates the nulls to default.
        /// </summary>
        /// <param name="row">The row.</param>
        public static void UpdateNullsToDefault(this DataRow row)
        {
            if (row != null)
            {
                if (row.CanRead())
                {
                    foreach (DataColumn c in row.Table.Columns)
                    {
                        if (c.DefaultValue != null && c.DefaultValue != DBNull.Value)
                        {
                            if (row[c].IsNull())
                            {
                                row[c] = c.DefaultValue;
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Updates the nulls to default.
        /// </summary>
        /// <param name="table">The table.</param>
        public static void UpdateNullsToDefault(this DataTable table)
        {
            if (table != null)
            {
                DataRow[] rows = table.Select();
                foreach (DataRow r in rows)
                    r.UpdateNullsToDefault();
            }
        }
        /// <summary>
        /// Updates the nulls to default.
        /// </summary>
        /// <param name="dataset">The ds.</param>
        public static void UpdateNullsToDefault(this DataSet dataset)
        {
            if (dataset != null)
            {
                foreach (DataTable d in dataset.Tables)
                    d.UpdateNullsToDefault();
            }
        }
        /// <summary>
        /// Determines whether [is realy modified] [the specified row].
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="includeChildRecords">if set to <c>true</c> [include child records].</param>
        /// <returns></returns>
        public static bool IsRealyModified(this DataRow row, bool includeChildRecords = false)
        {
            if (row != null)
            {
                if(row.RowState == DataRowState.Deleted || row.RowState == DataRowState.Detached || row.RowState == DataRowState.Added)
                    return true;
                if (row.RowState != DataRowState.Unchanged)
                {
                    foreach (DataColumn c in row.Table.Columns)
                    {
                        if (!row[c].IsTheSame(row[c, DataRowVersion.Original]))
                        {
                            if (c.DataType == typeof(string))
                            {
                                if (row[c].AsString() != row[c, DataRowVersion.Original].AsString())
                                    return true;
                            }
                            else
                                return true;
                        }
                    }
                }
            }
            if (includeChildRecords)
            {
                DataRow[] childRows;
                foreach (DataRelation rel in row.Table.ChildRelations)
                {
                    childRows = row.GetChildRows(rel);
                    if (childRows != null)
                        foreach (DataRow r in childRows)
                            if (r.IsRealyModified(true))
                                return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Determines whether [has modified child rows] [the specified row].
        /// </summary>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        public static bool HasModifiedChildRows(this DataRow row)
        {
            if (row != null)
            {
                DataRow[] childRows;
                foreach (DataRelation rel in row.Table.ChildRelations)
                {
                    childRows = row.GetChildRows(rel);
                    if (childRows != null)
                        foreach (DataRow r in childRows)
                            if (r.IsRealyModified(true))
                                return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Determines whether this instance can read the specified row.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        public static bool CanRead(this DataRow row)
        {
            if (row == null || row.RowState == DataRowState.Deleted)
                return false;
            return true;
        }
        /// <summary>
        /// Columns the changed.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <returns></returns>
        public static bool ColumnChanged(this DataRow row, string column)
        {
            if (row.RowState == DataRowState.Deleted ||
                row.RowState == DataRowState.Added || 
                row.RowState == DataRowState.Detached)
                return true;
            if (row.RowState == DataRowState.Modified)
                return !row[column, DataRowVersion.Original].IsTheSame(row[column, DataRowVersion.Current]);
            return false;
        }
        /// <summary>
        /// Rows the exists.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="filterExpression">The filter expression.</param>
        /// <returns></returns>
        public static bool RowExists(this DataTable table, string filterExpression)
        {
            return table.SelectFirstRow(filterExpression) != null;
        }

        /// <summary>
        /// Determines whether [has changes withreference] [the specified row].
        /// </summary>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        public static bool HasChangesWithreference(this DataRow row)
        {
            if (row == null)
                return false;
            if (row.RowState != DataRowState.Unchanged && row.RowState != DataRowState.Modified)
                return true;
            if (row.RowState == DataRowState.Modified)
            {
                foreach (DataColumn col in row.Table.Columns)
                {
                    if (row.ColumnChanged(col.ColumnName))
                        return true;
                }
            }
            if (row.Table != null && row.Table.DataSet != null)
            {
                foreach (DataRelation rel in row.Table.DataSet.Relations)
                {
                    if (rel.ParentTable == row.Table)
                    {
                        if (HasChangesInChildRows(row, rel))
                            return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Determines whether [has referenced rows] [the specified row].
        /// </summary>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        public static bool HasReferencedRows(this DataRow row)
        {
            bool thesame = false;
            foreach (DataRelation rel in row.Table.DataSet.Relations)
            {
                if (rel.ParentTable == row.Table)
                {
                    foreach (DataRow r in rel.ChildTable.Rows)
                    {
                        
                        if (r.RowState != DataRowState.Deleted && r.RowState != DataRowState.Detached)
                        {
                            thesame = true;
                            for (int i = 0; i < rel.ChildColumns.Length; i++)
                            {
                                if (!r[rel.ChildColumns[i]].IsTheSame(row[rel.ParentColumns[i]]))
                                {
                                    thesame = false;
                                    break;
                                }
                            }
                            if (thesame)
                                return true;
                        }
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Determines whether [has changes in child rows] [the specified parent].
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="rel">The relative.</param>
        /// <returns></returns>
        private static bool HasChangesInChildRows(DataRow parent, DataRelation rel)
        {
            DataRow[] rows = rel.ChildTable.Select(null, null, DataViewRowState.CurrentRows);
            foreach (DataRow r in rows)
            {
                for (int i = 0; i < rel.ParentColumns.Length; i++)
                {
                    bool thesame = true;
                    if (!parent[rel.ParentColumns[i]].IsTheSame(r[rel.ChildColumns[i]]))
                    {
                        thesame = false;
                        break;
                    }
                    if (thesame)
                    {
                        if (r.HasChangesWithreference())
                            return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Deletes the row with relation.
        /// </summary>
        /// <param name="row">The row.</param>
        public static void DeleteRowWithRelation(this DataRow row)
        {
            if (row != null && row.Table != null && row.Table.DataSet != null)
            {
                foreach (DataRelation rel in row.Table.DataSet.Relations)
                {
                    if (rel.ParentTable == row.Table)
                    {
                        DeleteChildRows(row, rel);
                    }
                }
            }
            row.Delete();
        }
        /// <summary>
        /// Deletes the child rows.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="rel">The relative.</param>
        private static void DeleteChildRows(DataRow parent, DataRelation rel)
        {
            DataRow[] rows = parent.GetChildRows(rel);
            foreach (DataRow r in rows)
            {
                 
                if (r.RowState != DataRowState.Deleted)
                    r.DeleteRowWithRelation();
            }
        }

        /// <summary>
        /// Selects the first row.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="filterExpression">The filter expression.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="recordStates">The record states.</param>
        /// <returns></returns>
        public static DataRow SelectFirstRow(this DataTable table, string filterExpression = null, string sort = null, DataViewRowState recordStates = DataViewRowState.CurrentRows)
        {
            if (table != null)
            {
                DataRow[] rows = null;
                try
                {
                    rows = table.Select(filterExpression, sort, recordStates);
                }
                catch (Exception ex)
                {
                    ex.LogInternal();
                }
                if (rows!=null && rows.Length > 0)
                    return rows[0];
            }
            return null;
        }
        /// <summary>
        /// Selects to table.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="filterExpression">The filter expression.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="recordStates">The record states.</param>
        /// <returns></returns>
        public static DataTable SelectToTable(this DataTable table, string filterExpression = null, string sort = null, DataViewRowState recordStates = DataViewRowState.CurrentRows)
        {
            if (table != null)
            {
                DataRow[] rows = null;
                try
                {
                    rows = table.Select(filterExpression, sort, recordStates);
                }
                catch (Exception ex)
                {
                    ex.LogInternal();
                }
                DataTable dt = table.Clone();
                if (rows != null)
                {
                    foreach (DataRow r in rows)
                        dt.ImportRow(r);
                }
                return dt;
            }
            return null;
        }
        /// <summary>
        /// Deletes the rows.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="filterExpression">The filter expression.</param>
        public static void DeleteRows(this DataTable table, string filterExpression=null)
        {
            if (table != null)
            {
                DataRow[] rows = null;
                try
                {
                    rows = table.Select(filterExpression);
                }
                catch (Exception ex)
                {
                    ex.LogInternal();
                }
                if (rows != null)
                    foreach (DataRow r in rows)
                        r.DeleteRowWithRelation();
            }
        }
        /// <summary>
        /// Clears the errors.
        /// </summary>
        /// <param name="table">The table.</param>
        public static void ClearErrors(this DataTable table)
        {
            if (table != null)
                foreach (DataRow r in table.Rows)
                    r.ClearErrors();
        }

        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="groupColumns">The group columns.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <param name="filter">The filter.</param>
        /// <param name="onlyModified">if set to <c>true</c> [only modified].</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string[] groupColumns, string errorToSet, string filter, bool onlyModified)
        {
            DataRow[] allCurrentRows = table.Select(filter, null, DataViewRowState.CurrentRows);
            DataRow[] modified = table.Select(filter, null, DataViewRowState.ModifiedCurrent | DataViewRowState.Added);
            if (onlyModified)
                return ValidateColUnique(modified, allCurrentRows, col, groupColumns, errorToSet);
            else
                return ValidateColUnique(allCurrentRows, allCurrentRows, col, groupColumns, errorToSet);
        }
        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="groupColumns">The group columns.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <param name="filter">The filter.</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string[] groupColumns, string errorToSet, string filter)
        {
            return ValidateColUnique(table, col,groupColumns, errorToSet, filter, true);
        }
        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="groupColumns">The group columns.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string[] groupColumns, string errorToSet)
        {
            return ValidateColUnique(table, col,groupColumns, errorToSet, null, true);
        }

        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <param name="filter">The filter.</param>
        /// <param name="onlyModified">if set to <c>true</c> [only modified].</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string errorToSet, string filter, bool onlyModified)
        {
            return ValidateColUnique(table, col, null, errorToSet, filter, onlyModified);
        }
        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <param name="filter">The filter.</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string errorToSet, string filter)
        {
            return ValidateColUnique(table, col, errorToSet, filter, true);
        }
        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="col">The col.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <returns></returns>
        public static bool ValidateColUnique(this DataTable table, string col, string errorToSet)
        {
            return ValidateColUnique(table, col, errorToSet, null, true);
        }
        /// <summary>
        /// Validates the table.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="rules">The rules.</param>
        /// <param name="assignFilterToRules">if set to <c>true</c> [assign filter to rules].</param>
        /// <param name="assignStopOnFirstErrorToRules">if set to <c>true</c> [assign stop on first error to rules].</param>
        /// <param name="filter">The filter.</param>
        /// <param name="onlyModified">if set to <c>true</c> [only modified].</param>
        /// <param name="stopOnFirstError">if set to <c>true</c> [stop on first error].</param>
        /// <returns></returns>
        public static bool ValidateTable(this DataTable table, ValidationRule[] rules, bool assignFilterToRules = true, bool assignStopOnFirstErrorToRules = true, string filter = null, bool onlyModified = true, bool stopOnFirstError = true)
        {
            bool result = true;
            if (rules != null)
            {
                DataRow[] rows = null;
                if (onlyModified)
                    rows = table.Select(filter, null, DataViewRowState.Added | DataViewRowState.ModifiedCurrent);
                else
                    rows = table.Select(filter, null, DataViewRowState.CurrentRows);
                foreach (var rule in rules)
                {
                    if (assignFilterToRules && rule is IFilterValidationRule)
                        ((IFilterValidationRule)rule).Filter = filter;
                    
                    if (assignStopOnFirstErrorToRules && rule is IStopOnFirstErrorValidationRule)
                        ((IStopOnFirstErrorValidationRule)rule).StopOnFirstError = stopOnFirstError;
                    foreach (var row in rows)
                    {
                        result = rule.ValidateRow(row);
                        if (!result && stopOnFirstError)
                            break;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Validates the col unique.
        /// </summary>
        /// <param name="rowsToCheck">The rows to check.</param>
        /// <param name="allrows">The allrows.</param>
        /// <param name="col">The col.</param>
        /// <param name="groupColumns">The group columns.</param>
        /// <param name="errorToSet">The error to set.</param>
        /// <returns></returns>
        private static bool ValidateColUnique(DataRow[] rowsToCheck, DataRow[] allrows, string col ,string[] groupColumns, string errorToSet)
        {
            bool result = true;
            foreach (DataRow r in rowsToCheck)
            {
                foreach (DataRow r2 in allrows)
                {
                    if (r2 == r)
                        continue;
                    bool thesame = r2[col].IsTheSame(r[col]);
                    if (thesame && groupColumns != null)
                    {
                        foreach(string s in groupColumns)
                            thesame &= r2[s].IsTheSame(r[s]);
                    }
                    if (thesame)
                    {
                        r2.SetColumnError(col, errorToSet);
                        r.SetColumnError(col, errorToSet);
                        result = false;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Merges the data set.
        /// </summary>
        /// <param name="ds">The ds.</param>
        /// <param name="dsToMerge">The ds to merge.</param>
        /// <param name="clearTables">if set to <c>true</c> [clear tables].</param>
        /// <param name="tables">The tables.</param>
        public static void MergeDataSet(this DataSet ds, DataSet dsToMerge, bool clearTables, params string[] tables)
        {
            if (tables != null)
            {
                ds.EnforceConstraints = false;
                foreach (string str in tables)
                {
                    if (clearTables)
                    {
                        if (ds.Tables.Contains(str))
                            ds.Tables[str].Clear();
                    }
                    if (ds.Tables.Contains(str) && dsToMerge.Tables.Contains(str))
                        ds.Tables[str].Merge(dsToMerge.Tables[str]);
                }
                ds.EnforceConstraints = true;
            }
        }
        /// <summary>
        /// Writes the XML string.
        /// </summary>
        /// <param name="ds">The ds.</param>
        /// <param name="mode">The mode.</param>
        /// <returns></returns>
        public static string WriteXmlString(this DataSet ds, XmlWriteMode mode = XmlWriteMode.IgnoreSchema)
        {
            StringWriter sw = new StringWriter();
            ds.WriteXml(sw, mode);
            string result = sw.ToString();
            return result;
        }
        /// <summary>
        /// Reads the XML string.
        /// </summary>
        /// <param name="ds">The ds.</param>
        /// <param name="xml">The XML.</param>
        /// <param name="mode">The mode.</param>
        public static void ReadXmlString(this DataSet ds, string xml, XmlReadMode mode = XmlReadMode.InferSchema)
        {
            StringReader sr = new StringReader(xml);
            ds.ReadXml(sr, mode);
        }

        /// <summary>
        /// Trims value in all columns in all rows
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <param name="filter">Filter for rows to trim</param>
        /// <param name="onlyModified">Only added and modified rows will be trimmed</param>
        /// <param name="columns">columns</param>
        /// <returns>
        /// true if any row was modified
        /// </returns>
        public static bool Trim(this DataTable dt, string filter = null, bool onlyModified = true, params string[] columns)
        {
            bool result = false;
            if (dt != null)
            {
                DataRow[] rows;
                if (onlyModified)
                    rows = dt.Select(filter, null, DataViewRowState.Added | DataViewRowState.ModifiedCurrent);
                else
                    rows = dt.Select(filter, null, DataViewRowState.CurrentRows);

                if (rows != null && rows.Length > 0)
                {
                    string val, valTrimmed;
                    foreach (DataRow r in rows)
                    {
                        foreach (string col in columns)
                        {
                            val = r[col].ToString();
                            valTrimmed = val.Trim();
                            if (val != valTrimmed)
                            {
                                r[col] = valTrimmed;
                                result = true;
                            }
                        }
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// Trims value in all columns in all modified rows
        /// </summary>
        /// <param name="dt">The dt.</param>
        /// <param name="columns">columns</param>
        /// <returns>
        /// true if any row was modified
        /// </returns>
        public static bool Trim(this DataTable dt, params string[] columns)
        {
            return dt.Trim(null, true, columns);
        }

        /// <summary>
        /// Validates table againts all rules
        /// </summary>
        /// <param name="dt">DataTable to validate</param>
        /// <param name="rules">Rules</param>
        /// <param name="filter">Filter for rows to validate</param>
        /// <param name="onlyModified">If true unchanged rows will be skipped</param>
        /// <param name="stopOnFirstError">If true validation will stop on first fail</param>
        /// <param name="ValidationFailed">Action to execute if validation fails</param>
        /// <param name="ValidationPassed">Action tu execute if validation succeeded</param>
        /// <returns></returns>
        public static bool ValidateTable(this DataTable dt, IEnumerable<ValidationRule> rules, string filter = null, bool onlyModified = true, bool stopOnFirstError = true, Action<ValidationRule, DataRow> ValidationFailed = null, Action<ValidationRule, DataRow> ValidationPassed = null)
        {
            DataRow[] rows =null;
            if(onlyModified)
                rows = dt.Select(filter,null, DataViewRowState.Added| DataViewRowState.ModifiedCurrent);
            else
                rows = dt.Select(filter,null, DataViewRowState.CurrentRows);
            bool result = true;
            foreach (DataRow r in rows)
            {
                result = r.ValidateRow(rules, stopOnFirstError);
                if (stopOnFirstError)
                    break;
            }
            return result;
        }

        /// <summary>
        /// Validates row agaits rules
        /// </summary>
        /// <param name="row">Row to validate</param>
        /// <param name="rules">Rules</param>
        /// <param name="stopOnFirstError">If true validation will stop on first fail</param>
        /// <param name="ValidationFailed">Action to fire if validation failes</param>
        /// <param name="ValidationPassed">Action to fire iv validation is succeded</param>
        /// <returns></returns>
        public static bool ValidateRow(this DataRow row, IEnumerable<ValidationRule> rules, bool stopOnFirstError = true, Action<ValidationRule, DataRow> ValidationFailed = null, Action<ValidationRule, DataRow> ValidationPassed = null)
        {
            bool result = true;
            foreach (ValidationRule rule in rules)
            {
                if (!rule.ValidateRow(row))
                {
                    result = false;
                    if (ValidationFailed != null)
                        ValidationFailed(rule, row);
                    if (stopOnFirstError)
                        break;
                }
                if (ValidationPassed != null)
                    ValidationPassed(rule, row);
            }
            return result;
        }
    }
}
